#ifndef _CELLS_H
#define _CELLS_H
#include "typs.h"
#include "pathfind.h"
#include "array.hpp"
#include "errorm.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

enum BRIDGE_TYPES {
  BRIDGE_HIGHWAY,
  BRIDGE_COUNTRY,
  BRIDGE_RAIL,
  N_BRIDGE_TYPES
};

enum BRIDGE_DIRECTIONS {
  BRIDGE_DIR_HORIZONTAL,        // ---
  BRIDGE_DIR_VERTICAL,          //  |
  BRIDGE_DIR_DIAGONAL_UP,       //  /
  BRIDGE_DIR_DIAGONAL_DOWN      //  backslash
};

struct BridgeInfo {
  int   startPos;   // offset in map (== colStart + rowStart*mapWidth)
  int   nCells;     // length
  int   incPos;     // startPos+incPos is offset of second cell of bridge,
                    // startPos+2*incPos is offset of third cell of bridge, etc.
  int   type;       // enum BRIDGE_TYPES
  int   damaged;

  int   dir;        // enum BRIDGE_DIRECTIONS

  BridgeInfo() {  
    nCells = 0;    
  }

  void set(ExternalBridgeInfo &e, int mapWd, int mapHg, int br_idx) {
    // Is bridge inside of map? (note!: bridges requires TWO safe border cell
    // lines around of map, because of alghorithm of making unpassable cells
    // near diagonal bridges)
    if( (e.colStart<=1) || (e.colStart>=mapWd-2) ||
        (e.rowStart<=1) || (e.rowStart>=mapHg-2) ) {
      ERROR("BridgeInfo: bridge %i is out of map.",br_idx);
    }

    // Convert external type to our internal type
    switch(e.type) {
    case EXTERNAL_BRIDGE_HIGHWAY:
      type = BRIDGE_HIGHWAY;
      break;
    case EXTERNAL_BRIDGE_COUNTRY:
      type = BRIDGE_COUNTRY;
      break;
    case EXTERNAL_BRIDGE_RAIL:
      type = BRIDGE_RAIL;
      break;
    default:
      ERROR("BridgeInfo: bridge %i have wrong type.",br_idx);
    }

    damaged = e.damaged;

    // Compute startPos
    startPos = mapWd*e.rowStart + e.colStart;

    // Compute nCells & incPos & dir
    int dCol = e.colEnd - e.colStart;
    int dRow = e.rowEnd - e.rowStart;
    if(dRow==0) {         // horizontal direction
      if(dCol==0) {
        ERROR("BridgeInfo: bridge %i have zero length.",br_idx);
      }
      nCells = abs(dCol) + 1;
      incPos = (dCol > 0 ) ? +1 : -1;
      dir = BRIDGE_DIR_HORIZONTAL;
    }else if(dCol==0) {   // vertical direction
      nCells = abs(dRow) + 1;
      incPos = (dRow > 0) ? +mapWd : -mapWd;
      dir = BRIDGE_DIR_VERTICAL;
    }else {               // diagonal direction
      if(abs(dCol) != abs(dRow)) {
        ERROR("BridgeInfo: bridge %i have wrong direction.",br_idx);
      }
      nCells = abs(dRow) + 1;
      incPos  = (dRow > 0) ? +mapWd : -mapWd;
      incPos += (dCol > 0) ? +1     : -1;
      dir = ( ((dCol>0)&&(dRow<0)) || ((dCol<0)&&(dRow>0)) )
          ? BRIDGE_DIR_DIAGONAL_UP : BRIDGE_DIR_DIAGONAL_DOWN;
    }
  }
};
struct StaticCell {
  ubyte water       : 1;     // any water
  ubyte deepwater   : 1;     // deep water (when==1, then water also ==1)
  ubyte montain     : 1;
  ubyte countryRoad : 1;
  ubyte railRoad    : 1;
  ubyte highwayRoad : 1;
  ubyte bridge      : 1;     // used to check for bridge crossings (yet)
  ubyte forest      : 1;
  //ubyte _unised_    : 1;
  void reset() {
    water       = 0;
    deepwater   = 0;
    montain     = 0;
    countryRoad = 0;
    railRoad    = 0;
    highwayRoad = 0;
    bridge      = 0;
	forest      = 0;
  }
};

struct DynamicalCell {
  ubyte free        : 1;
  ubyte parent      : 3;
  ubyte fastway     : 1;
  ubyte unpassable  : 1;
  ubyte notendpoint : 1;     // used only if !unpassable
  ubyte goodbridge  : 1;     // if here is passable and matched-type bridge

  void setForVehicle(const StaticCell &st) {
    if(st.countryRoad || st.highwayRoad) {
      free = 1;
      unpassable = 0;
      notendpoint = 0;
      fastway = 1;
      parent = 0;
      goodbridge = 0;
    }else if(st.water || st.montain) {
      free = 0;
      unpassable = 1;
    }else {
      free = 1;
      unpassable = 0;
      notendpoint = 0;
      fastway = 0;
      parent = 0;
      goodbridge = 0;
    }
  }

  void setForTrooper(const StaticCell &st) {
    if(st.countryRoad || st.highwayRoad) {
      free = 1;
      unpassable = 0;
      notendpoint = 0;
      fastway = 1;
      parent = 0;
      goodbridge = 0;
    }else if(st.water) {
      free = 0;
      unpassable = 1;
    }else {
      free = 1;
      unpassable = 0;
      notendpoint = 0;
      fastway = 0;
      parent = 0;
      goodbridge = 0;
    }
  }

  void setForTrain(const StaticCell &st) {
    if(st.railRoad) {
      free = 1;
      unpassable = 0;
      notendpoint = 0;
      fastway = 1;
      parent = 0;
      goodbridge = 0;
    }else {
      free = 0;
      unpassable = 1;
    }
  }

  void setForShip(const StaticCell &st) {
    if(st.deepwater) {
      free = 1;
      unpassable = 0;
      notendpoint = 0;
      fastway = 0;
      parent = 0;
      goodbridge = 0;
    }else {
      free = 0;
      unpassable = 1;
    }
  }

  void setForBuilding(const StaticCell &st) {
    if(st.water || st.countryRoad || st.highwayRoad || st.railRoad) {
      free = 0;
      unpassable = 1;
    }else {
      free = 1;
      unpassable = 0;
      notendpoint = 0;
      fastway = 0;
      parent = 0;
      goodbridge = 0;
    }
  }

  void setFor_(const StaticCell &st, int moverType) {
    switch(moverType) {
    case MOVER_VEHICLE:
      setForVehicle(st);
      break;
    case MOVER_TROOPER:
      setForTrooper(st);
      break;
    case MOVER_SHIP:
      setForShip(st);
      break;
    case MOVER_TRAIN:
      setForTrain(st);
      break;
    case MOVER_BUILDING:
      setForBuilding(st);
      break;
    default:
      InternalERROR("DynaCell: Unknown mover type");
    } // switch
  }
};



struct Ring {

#define RINGSZ   (32*1024)
#define RINGMASK (RINGSZ-1)

 private:
  int buff[RINGSZ];
  int head;  // first stored item
  int tail;  // place to put new item

 public:
  int overflow;

  Ring() {
    clear();
  }
  void clear() {
    head = tail = 0;
    overflow = 0;
  }

  int N() {
    return (tail-head) & RINGMASK;
  }

  int extract() {
    int item = buff[head];
    head = (head+1) & RINGMASK;
    return item;
  }

  int get() {
    return buff[head];
  }

  int get(int offs) {
    return buff[(head+offs) & RINGMASK];
  }

  int getInc(int offs, int addval) {
    return (buff[(head+offs) & RINGMASK] += addval);
  }

  void inc(int offs, int addval) {
    buff[(head+offs) & RINGMASK] += addval;
  }

  int add(int item) {
    if(N() >= (3*RINGSZ)/4) {     // (3*RINGSZ)/4 instead of RINGSZ-1 is
      overflow = 1;               // for safety margin (because detection
      return 0;                   // of overflow isn't instant).
    }
    buff[tail] = item;
    tail = (tail+1) & RINGMASK;
    return 1;
  }

  void del() {
    if(N() <= 0)
      InternalERROR("ring buffer: del");
    head = (head+1) & RINGMASK;
  }
};

#endif